package asteroids.model;
/**
 * ALS EEN SHIP NAAR NOWORLD GAAT MOETEN ALLE BULLETS VERWIJDERD WORDEN!!!!!!!!!
 */

import ModelExceptions.IllegalDensityException;
import ModelExceptions.IllegalRadiusException;
import ModelExceptions.OutOfBoundsException;
import asteroids.Util;
import be.kuleuven.cs.som.annotate.Basic;
import be.kuleuven.cs.som.annotate.Raw;

public class Bullet extends CollisionObject{
	/**
	 * 
	 * @param xCoordinate
	 * @param yCoordinate
	 * @param xVelocity
	 * @param yVelocity
	 * @param radius
	 * @param ship
	 * @param density
	 * @throws OutOfBoundsException
	 * @throws IllegalRadiusException
	 * @throws IllegalDensityException
	 * @post	If radius ,xVelocity and yVelocity are valid inputs, then there will be made an object of the class ship,
	 * 			with the given values
	 * 			|if(isValidVelocity(xVelocity,yVelocity)==true && isValidRadius(radius) == true && isValidCoordinate(xCoordinate) && isValidCoordinate(yCoordinate)){
	 * 			new.getXCoordinate() == xCoordinate
	 * 			new.getYCoordinate() == yCoordinate
	 * 			new.getXVelocity() == xVelocity
	 * 			new.getYVelocity() == yVelocity
	 * 			new.getRadius() == radius
	 * 			new.getAngle() == angle
	 * @invar	The radius may not exceed the radiusConstraint
	 * 			|isValidRadius(radius) == true
	 * @invar	the velocity may never exceed 300000 km/s
	 * 			|isValidVelocity(xVelocity,yVelocity) == true
	 * @invar	The position has to be in the 2d-field
	 * 			|isValidCoordinate(xCoordinate) == true && isValidCoordinate(yCoordinate) == true
	 */
	public Bullet(double xCoordinate, double yCoordinate, double xVelocity, double yVelocity, double radius,Ship ship,double density) throws OutOfBoundsException, IllegalRadiusException, IllegalDensityException {
		super(xCoordinate, yCoordinate, xVelocity, yVelocity, radius);
		setMinimumDensity(0);
		setMaximumDensity(Double.MAX_VALUE);
		if(isValidDensity(density)){
			this.density = density;
		}
		else {throw new IllegalDensityException("That is an invalid density");}  
		setMass(radius);
		this.bulletSourceShip = ship;
		setWorld(ship.getWorld());


	}


	//TODO ik zou hier nog een tweede constructor willen die gwn de standaard density aanmaakt, want er is nergens gezegd dat deze density later niet kan veranderen, gwn eerder van nu voor de eenvoud gebruiken we voor elk van dit deze density, maak hier eerder gwn de default constructor van. Probleem is dat deze methode de constructor van collisionobject wilt gebruiken
	public Bullet(double xCoordinate, double yCoordinate, double xVelocity, double yVelocity, double radius,Ship ship) throws OutOfBoundsException, IllegalRadiusException {
		super(xCoordinate, yCoordinate, xVelocity, yVelocity, radius);
		setMinimumDensity(0);
		setMaximumDensity(Double.MAX_VALUE);
		this.density = getStandardDensity();
		setMass(radius);
		this.bulletSourceShip = ship;
		setWorld(ship.getWorld());


	}
	@Basic
	public double getStandardBulletRadius() {
		return standardBulletRadius;
	}
	private double standardBulletRadius = 3;

	/**
	 * 
	 * @return	||if(isValidObject()  && isInWorld()){
	 * 			||result == true}
	 */

	@Raw
	public boolean isValidBullet(){
		if(!isValidObject()  || !isInWorld())//&& getBulletSource()!= null && getBulletSource().hasAsBullet(this))
			return false;
		return true;
	}


	@Override @Basic
	protected double getMinimumMass() {
		return 0;
	}

	@Override @Basic
	protected double getMaximumMass() {
		return Double.POSITIVE_INFINITY;
	}

	@Override @Basic
	protected void setMass(double radius){
		this.mass = Math.pow(radius, 3)*4/3*Math.PI*getDensity();
	}
	//TODO laten zien dat het een nieuwe bullet is of niet?
	/**
	 * @param bullet 	The Object you want know the collisionposition of
	 * @param timelapse	The time left before the collision
	 * @return			||double newXCoordinate = bullet.getXVelocity()*timeLapse + bullet.getXCoordinate();
						||double newYCoordinate = bullet.getYVelocity()*timeLapse + bullet.getYCoordinate();
						||Bullet resultBullet = new Bullet(newXCoordinate,newYCoordinate,bullet.getXVelocity(),bullet.getYVelocity(),bullet.getRadius(),((Bullet) bullet).getShip());
						||return resultBullet;
	}
	 */
//	@Override 
//	protected CollisionObject getObjectAtColissionPosition(double timeLapse) {
//		double newXCoordinate = this.getXVelocity()*timeLapse + this.getXCoordinate();
//		double newYCoordinate = this.getYVelocity()*timeLapse + this.getYCoordinate();
//		Bullet resultBullet = new Bullet(newXCoordinate,newYCoordinate,this.getXVelocity(),this.getYVelocity(),this.getRadius(),((Bullet) this).getShip());
//		return resultBullet;
//	}
	@Basic
	public Ship getShip(){
		return this.bulletSourceShip;
	}
	/**
	 * 
	 * @param ship	The ship you want to check
	 * @return	||if(ship.isValidObject() && ship.isInWorld())
	 * 			||result == true
	 */
	public boolean canHaveAsSourceShip(Ship ship){
		//TODO
		if(ship.isValidObject() && ship.isInWorld())
			return true;
		return false;
	}


	
	@Basic
	private double getDensity(){
		return this.density;
	}

	/**
	 * 
	 * @param density 	The density you want to check
	 * @return			||if(Util.fuzzyLessThanOrEqualTo(density, getMinimumDensity()) || !Util.fuzzyLessThanOrEqualTo(density, getMaximumDensity())|| Double.isNaN(density)){
	 * 					||result == true;}
	 */
	private boolean isValidDensity(double density) {
		if(Util.fuzzyLessThanOrEqualTo(density, getMinimumDensity()) || !Util.fuzzyLessThanOrEqualTo(density, getMaximumDensity())|| Double.isNaN(density)){
			return true;
		}
		return false;
	}
	@Basic
	private double getMaximumDensity(){
		return this.maximumDensity;
	}
	@Basic
	private double getMinimumDensity(){
		return this.minimumDensity;
	}
	@Basic
	private void setMaximumDensity(double maxDensity){
		this.maximumDensity = maxDensity;
	}
	@Basic
	private void setMinimumDensity(double minDensity){
		this.minimumDensity = minDensity;
	}
	@Basic
	private double getStandardDensity(){
		return this.standardDensity;
	}
	private double minimumDensity;
	private double maximumDensity;


	//ik ben niet zeker dat density echt final moet zijn, en gebruik zeker getters en setters ipv direct de velden :)
	//TODO de density mag nie veranderen tijdens het spel volgens mij?


	private final double density;



	private double standardDensity = 7.8*Math.pow(10, 12);



	/**
	 * Return the source of <code>bullet</code>.
	 */
	@Basic
	public Ship getBulletSource(){
		return this.bulletSourceShip;
		//TODO
	}

	private Ship bulletSourceShip;
	
	/**
	 * 
	 * @param collisionObject	The object with what this will react
	 * @post					||if(collisionObject.isBullet()){
	 *							||	this.destroy()
	 *							||collisionObject.destroy()}
	 *@post						||if(collisionObject.isAsteroid()){
	 *							||	collisionObject.destroy();
	 *							||	this.destroy();}
	 *@post						||if(collisionObject.isShip()){
								||	if(collisionObject != this.getBulletSource() && this.getBulletSource() != null){
								||		this.destroy();
								||		collisionObject.destroy();}
								||	else{this.destroy();}
		}
	 */
	@Override
	public void collisionReactionWith(CollisionObject collisionObject) {
		if(collisionObject.isBullet()){
			this.destroy();
			collisionObject.destroy();
		}
		if(collisionObject.isAsteroid()){
			collisionObject.destroy();
			this.destroy();
		}
		if(collisionObject.isShip()){
			if(collisionObject != this.getBulletSource() && this.getBulletSource() != null){
				this.destroy();
				collisionObject.destroy();
			}
			else{this.destroy();}
		}
	}
	/**
	 * 
	 * @post	||this.destroy()
	 * 			||this.getBulletSource() == null;
	 */
	public void shipDestroy(){
		if(!this.isDestroyed())
			super.destroy();
		bulletSourceShip = null;
	}
	/**
	 * 
	 * @post	||this.destroy();
	 * 			||this.getBulletSource().hasAsBullet(this) == false;
	 * 			||this.getBulletSource() == null;
	 */
	@Override
	public void destroy(){
		if(!this.isDestroyed())
			getBulletSource().removeAsBullet(this);
		super.destroy();
		bulletSourceShip = null;
	}

	@Override
	public String toString(){
		return "This is a bullet with \n" +
				//"Homeship: \t: " +getBulletSource() +"\n" + 
				super.toString()+
				"\n";
	}
	@Basic
	public int getMaxNbOfBounces(){
		return this.maxNbOfBounces;
	}
	@Basic
	public void setMaxNbOfBounces(int max){
		this.maxNbOfBounces = max;
	}
	int maxNbOfBounces =  1;
	@Basic
	public int getNbOfBounces(){
		return this.nbOfBounces;
	}
	@Basic
	public void setNbOfBounces(int nbOfBounces){
		this.nbOfBounces = nbOfBounces;
	}
	/**
	 * 
	 * @post ||(new this).getNbOfBounces == this.getNbOfBounces() + 1
	 */
	public void incrementNbOfBounces(){
		this.nbOfBounces = getNbOfBounces() + 1;
	}
	int nbOfBounces = 0;

	@Override
	protected CollisionObject clone() {
		return new Bullet(getXCoordinate(), getYCoordinate(), getXVelocity(), getYVelocity(), getRadius(), getBulletSource(), getDensity());
	}
}
